<template>
  <div class="calendar"
       v-click-outside>
    <input type="text"
           placeholder="选择日期"
           class="calendar_input"
           :value="formatDate"
           ref="input" />
    <span class="input_prefix">
      <i class="iconfont iconrili"></i>
    </span>

    <!-- 日历面板 -->
    <div class="calendar_box"
         v-if="isVisible">
      <span class="triangle"></span>
      <!--面板上部三角形-->
      <div class="calendar_header">
        <span @click="preYear">&lt;&lt;</span>
        <span @click="preMonth">&lt;</span>
        <span class="header_time">
          <span>{{time.year}}年</span>
          <span>{{time.month + 1}}月</span>
        </span>
        <span @click="nextMonth">&gt;</span>
        <span @click="nextYear">&gt;&gt;</span>
      </div>
      <div class="calendar_content">
        <span v-for="j in 7"
              :key="`_${j}`"
              class="cell">
          {{weekDays[j - 1]}}
        </span>
        <div v-for="i in 6"
             :key="i">
          <!--从1开始循环-->
          <span v-for="j in 7"
                :key="j"
                class="cell"
                :class="[
                        {
                           notCurrentMonth: !isCurrentMonth(visibleDays[(i -1) * 7 + (j -1)])
                        },
                        {
                           today: isToday(visibleDays[(i -1) * 7 + (j -1)]) 
                        },
                        {
                          select: isSelect(visibleDays[(i -1) * 7 + (j -1)])
                        }
                    ]"
                @click="chooseDate(visibleDays[(i -1) * 7 + (j -1)])">
            <!--获取到每一天对应的日期date值进行显示-->
            {{visibleDays[(i -1) * 7 + (j -1)].getDate()}}
          </span>
        </div>

      </div>
      <!-- 日历底部 -->
      <div class="calendar_footer"
           @click="toToday">
        今天
      </div>
    </div>

  </div>
</template>
<script>
import util from '../utils/util'
export default {
  name: 'calendar',
  props: {
    value: {
      type: Date,
      default: function() {
        return new Date()
      },
    },
  },
  methods: {
    toToday() {
      this.time = util.getYearMonthDay(new Date())
    },
    preYear() {
      // 获取当前面板中的任意1天，比如当月1号对应的Date对象
      const someDayOfCurrentMonth = new Date(this.time.year, this.time.month, 1)
      const currentYear = someDayOfCurrentMonth.getFullYear()
      // 将当前面板中的某一天修改为上一个月中的某一天
      someDayOfCurrentMonth.setFullYear(currentYear - 1)
      // 从上一个月中的某一天获取对应的年月更新this.time
      this.time = util.getYearMonthDay(someDayOfCurrentMonth)
    },
    preMonth() {
      // 获取当前面板中的任意1天，比如当月1号对应的Date对象
      const someDayOfCurrentMonth = new Date(this.time.year, this.time.month, 1)
      const currentMonth = someDayOfCurrentMonth.getMonth()
      // 将当前面板中的某一天修改为上一个月中的某一天
      someDayOfCurrentMonth.setMonth(currentMonth - 1)
      // 从上一个月中的某一天获取对应的年月更新this.time
      this.time = util.getYearMonthDay(someDayOfCurrentMonth)
    },
    nextYear() {
      // 获取当前面板中的任意1天，比如当月1号对应的Date对象
      const someDayOfCurrentMonth = new Date(this.time.year, this.time.month, 1)
      const currentYear = someDayOfCurrentMonth.getFullYear()
      // 将当前面板中的某一天修改为上一个月中的某一天
      someDayOfCurrentMonth.setFullYear(currentYear + 1)
      // 从上一个月中的某一天获取对应的年月更新this.time
      this.time = util.getYearMonthDay(someDayOfCurrentMonth)
    },
    nextMonth() {
      // 获取当前面板中的任意1天，比如当月1号对应的Date对象
      const someDayOfCurrentMonth = new Date(this.time.year, this.time.month, 1)
      const currentMonth = someDayOfCurrentMonth.getMonth()
      // 将当前面板中的某一天修改为上一个月中的某一天
      someDayOfCurrentMonth.setMonth(currentMonth + 1)
      // 从上一个月中的某一天获取对应的年月更新this.time
      this.time = util.getYearMonthDay(someDayOfCurrentMonth)
    },
    isSelect(date) {
      // 传递面板上的时间，判断是不是用户选择的日期
      // 获取面板上日期对应的年、月、日
      const { year, month, day } = util.getYearMonthDay(date)
      // 获取用户已选择时间对应的年、月、日
      const { year: y, month: m, day: d } = util.getYearMonthDay(this.value)
      return year === y && month === m && day === d
    },
    chooseDate(date) {
      // 日历面板上有42天，所以用户有可能选择了其他月份的时间，日历面板也需要进行相应的更新
      this.time = util.getYearMonthDay(date) // 更新this.time即可更新日历面板显示的年月，从而更新42天
      this.$emit('input', date)
      // this.blur()
    },
    isCurrentMonth(date) {
      // 判断传递的日期是否属于当月
      // 获取传递时间对应的年月
      const { year, month } = util.getYearMonthDay(date)
      // 与日历面板显示年、月进行比较，如果年月相同，那么是当月时间
      return year === this.time.year && month === this.time.month
    },
    isToday(date) {
      // 判断传递的日期是否是今天
      // 获取传递时间对应的年月日
      const { year, month, day } = util.getYearMonthDay(date)
      // 获取今天时间对应的年月日
      const { year: y, month: m, day: d } = util.getYearMonthDay(new Date())
      return year === y && month === m && day === d
    },
  },
  computed: {
    formatDate() {
      const { year, month, day } = util.getYearMonthDay(this.value)
      return `${year}-${month + 1}-${day}`
    },
    visibleDays() {
      // 获取当月第一天对应的Date对象
      const firstDayOfMonth = new Date(this.time.year, this.time.month, 1)
      // 获取当月第一天对应的是星期几
      const week = firstDayOfMonth.getDay()
      // 获取42天中的第一天对应的Date对象，即每月1号对应的时间减去week天
      const startDay = firstDayOfMonth - week * 60 * 60 * 1000 * 24
      const days = []
      for (let i = 0; i < 42; i++) {
        // 循环出42天
        days.push(new Date(startDay + i * 60 * 60 * 1000 * 24))
      }
      return days
    },
  },
  data() {
    const { year, month } = util.getYearMonthDay(this.value) // 获取传递时间对应的年、月
    return {
      isVisible: false, // 控制面板是否可见
      time: { year, month }, // 定义time对象显示当前年、月
      weekDays: ['日', '一', '二', '三', '四', '五', '六'],
    }
  },
  directives: {
    // 添加指令对象
    clickOutside: {
      bind(el, binding, vnode) {
        const handler = e => {
          if (el.contains(e.target)) {
            // 如果点击的文本框，需要显示日历面板
            if (!vnode.context.isVisible) {
              // 如果isVisible为false则打开日历面板
              // console.log("vnode===>", vnode)
              vnode.context.isVisible = true
              e.target.focus()
            }
          } else {
            // 如果点击的不是文本框，而是文本框的外部
            if (vnode.context.isVisible) {
              // 如果isVisible为true则关闭日历面板
              vnode.context.isVisible = false
              e.target.blur()
            }
          }
        }
        el.handler = handler // 将事件处理函数保存到el上，即指令所在DOM上，方便解绑移除事件处理函数
        document.addEventListener('click', handler)
      },
      unbind(el) {
        document.removeEventListener('click', el.handler)
      },
    },
  },
}
</script>
<style scoped>
@font-face {
  font-family: 'iconfont';
  src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALwAAsAAAAAB8QAAAKkAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCcAqDUIMmATYCJAMICwYABCAFhG0HLhvKBhHVkz1kPwrj9qSlmDfJebNDpSCSnDR9XwTPox31fpKZzVo6SC3E6nqoP3dgB5dEPfs/Z9kkCxthinLICnUpv8BpduBOq3vTbgHwx73TvwIKZD6gnObY+KmLoy7cGtDeGEVWICmGmTeM3UR5ELchgB9JFCAdXZc7WAxgkQCyannogk3pMDXFgkVwS3Ya5BgOVu1XjwGO8vfLVygTCwpHA8pGlmwDaPmYB9P0Nu9vFkXgj2cBtH2ggQLAgEyU2obQYawAjZ8TM6TBuooFPuZ5H8pb7R8PBMQFFAYAkCDyzomPBadaqAAwrQYvA9d7FUNAjE0JAPM3ypkoP7adP3BRJICf6XcqgtUh6nRk8NnoOf4HL2C2nfcLKU1ztl/y9xfCyeoJlCWL6jga4tfK9kuT8TdMrd9Xo7LXufPOaEGhCaFBhR181BnHXefNP7jOrzDz3PP/oNCgD1jRIulutzbRt3aI1Ls/dTzaUODWxM88+8gjaAHAe2uoWPzAz3C/L2fd3GHDf+tvAHj17t4d7vHeBto5wN6mXeB38VvWGFcI9MrY/FKH4vJtL1SAH36AB7IrjPd9HZEQWwSr80VQ+JAIGksGaigF4OBPBbhYmsGPfLr3+xPOBjRifIE8dgsghHANFEHcAU0IT1BDeQcOUXwHlxDR4McUCT/RnyxJ4s6ayRUK0PvF2C8LhYzSCYqvFL4yl5NCTnsSN3EQLd3MJvdUEI+xpvkKbRGFisscd8J9lGUlVlwm5IseiVQjw1BlT9L9MocOtDO5QgHi/SKxXxaKNpdO7vVXCl+ZyzWkDvuTuImHRyx0zBboXla0Il3LI81XaCOiEMVljuwEC2UwViJV+bSEfNGJekSqEYZUT7WV6fMr8qfbBkAHgLrdgtUaw3EWAwA=')
    format('woff2');
}

.iconfont {
  font-family: 'iconfont' !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.iconrili:before {
  content: '\e72a';
}
.calendar {
  position: relative;
  width: auto;
}
.calendar_input {
  border: 1px solid #c0c4cc;
  padding: 0 30px;
  height: 40px;
  line-height: 40px;
  border-radius: 4px;
  outline: none; /* 去除边框外的轮廓 */
}
.calendar_input:focus {
  border: 1px solid #409eff;
}
.input_prefix {
  height: 100%;
  width: 25px;
  text-align: center;
  position: absolute;
  left: 5px;
  top: 0;
  color: #c0c4cc;
}
.input_prefix i {
  line-height: 40px;
}
.calendar_box {
  position: absolute;
  top: 50px;
  /* width: 400px; 暂时使用固定宽度和高度，后面会去除宽度和高度进行内容自适应现实 */
  /* height: 300px; */
  border: 1px solid #e4e7ed;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  border-radius: 4px;
}
.calendar_box .triangle {
  position: absolute;
  width: 0;
  height: 0;
  top: -14px;
  left: 25px;
  border: 7px solid transparent;
  border-bottom: 7px solid white;
}
.calendar_box::before {
  position: absolute;
  content: '';
  width: 0;
  height: 0;
  top: -16px;
  left: 24px;
  border: 8px solid transparent;
  border-bottom: 8px solid #e4e7ed;
}

.calendar_header {
  display: flex;
  justify-content: space-around;
  height: 30px;
  line-height: 30px;
  font-size: 14px;
  font-weight: 100;
}
.calendar_header span {
  cursor: pointer;
}
.header_time {
  box-sizing: border-box;
  width: 50%;
  padding: 0 25px;
  height: 30px;
  line-height: 30px;
  color: #606266;
  font-size: 16px;
  font-weight: 500;
  display: flex;
  justify-content: space-between;
}
.calendar_content .cell {
  display: inline-flex;
  width: 41px;
  height: 41px;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}

.notCurrentMonth {
  color: grey;
}
.today {
  background: red;
  color: white;
  border-radius: 4px;
}

.select {
  border: 1px solid pink;
  box-sizing: border-box;
  border-radius: 4px;
}

.calendar_footer {
  height: 30px;
  line-height: 30px;
  padding: 5px 0;
  border: 1px solid #e4e7ed;
  border-radius: 0 0 4px 4px;
  text-align: center;
  cursor: pointer;
}
</style>